/*
This codefile contains the functionality of the "ElevatorLogic" 
component, specified in the design report ("tussenrapport.doc").

Author:			Daan
Version: 		1
Last modified:	09-06-11
*/

// interface/facade implementation
void ElevatorLogic_GoTo (int floor)
{
	GoTo(floor);
}

void ElevatorLogic_CallEmergency ()
{
	CallEmergency();
}

void ElevatorLogic_ResumeOperations ()
{
	ResumeOperations();
}

void ElevatorLogic_Above (int floor)
{
	Above(floor);
}

void ElevatorLogic_At (int floor)
{
	At(floor);
}

void ElevatorLogic_Below (int floor)
{
	Below(floor);
}



// global variables for this component
const float MINIMUM_VISIT_TIME = 5.0f; // the minimum time in seconds to wait at each floor
const unsigned int VISIT_TIMER_ID = 1; // the ID of the timer we use to wait at each floor
const int HIGHEST_FLOOR = 3; // upper floor
const int LOWEST_FLOOR = 0; // lowest floor
const int FLOOR_COUNT = 4; // number of floors
BOOL floorCalls[4]; // which floors buttons have been pressed since the last visit there
int	currentFloor; // the current or last visited floor
BOOL isCabinMoving; // is the cabin moving to another floor right now?
BOOL direction; // UP = 1, DOWN = 0
BOOL preparingToStop; // is the cabin in the process of stopping at a floor (moving slowly)?
BOOL initializing; // at startup, the elevator first has to figure out where it is. this is set to true during this proces
BOOL emergency; // are we in emergency mode?


/*
this function is triggered when the system boots up. this function puts the elevator into an initializing state. while initializing, the cabin moves down slowly until it arrives at a floor. the system then knows where the cabin is and starts normal operations.
*/
void ElevatorLogic_Initialize ()
{
	int floor;
	// initialize global variables
	for (floor=LOWEST_FLOOR; floor<=HIGHEST_FLOOR; floor++)
	{
		floorCalls[floor] = FALSE;
	}
	currentFloor = 0;
	isCabinMoving = TRUE;
	direction = DOWN;
	preparingToStop = TRUE;
	initializing = TRUE;
	emergency = FALSE;
}

/*
this function is executed repeatedly at an arbitrary rate. it contains the operational logic for the cabin behavior. the global variables are used to deduce the state of the entire system, from which the next immediate action is determined and executed. elevator behavior is independent of the frequency of execution of this function. not all behavior is defined here, as other functions that are triggered by sensors or buttons also induce behavior.
*/
void ElevatorLogic_Operate ()
{
	int floor;
	BOOL weNeedToBeSomewhere = FALSE;
	BOOL weNeedToBeSomewhereAbove = FALSE;
	BOOL weNeedToBeSomewhereBelow = FALSE;
	
	if (emergency == TRUE)
	{
		HardwareIn_StopMoving();
		isCabinMoving = FALSE;
		time(500);
		HardwareIn_SetBuzzer();
		time(500);
		HardwareIn_UnsetBuzzer();
	}
	else if (initializing == TRUE)
	{
		HardwareIn_MoveDown(SLOW);
	}
	// if we're not moving, we need to be somewhere AND we've waited long enough, move in the direction we were already going, unless we reached an outer floor OR don't need to be anywhere in that direction
	else if (isCabinMoving == FALSE)
	{
		if (currentFloor == LOWEST_FLOOR)
		{
			direction = UP;
		}
		else if (currentFloor == HIGHEST_FLOOR)
		{
			direction = DOWN;
		}
		
		for (floor=LOWEST_FLOOR; floor<=HIGHEST_FLOOR; floor++)
		{
			if (floorCalls[floor] == TRUE)
			{
				weNeedToBeSomewhere = TRUE;
				if (floor > currentFloor)
				{
					weNeedToBeSomewhereAbove = TRUE;
				}
				else if (floor < currentFloor)
				{
					weNeedToBeSomewhereBelow = TRUE;
				}
			}
		}
		
		if (weNeedToBeSomewhere == TRUE)
		{
			// we use a timer, which is started when we stop at a floor, to wait a minimum while at each floor.
			if (CountdownTimer_HasTimerRunOutYet(VISIT_TIMER_ID) == TRUE)
			{
				isCabinMoving = TRUE;
				if (direction == UP && weNeedToBeSomewhereAbove == TRUE)
				{
					HardwareIn_MoveUp(SLOW);
				}
				else if (direction == DOWN && weNeedToBeSomewhereBelow == TRUE)
				{
					HardwareIn_MoveDown(SLOW);
				}
				else if (weNeedToBeSomewhereAbove == TRUE)
				{
					direction = UP;
					HardwareIn_MoveUp(SLOW);
				}
				else if (weNeedToBeSomewhereBelow == TRUE)
				{
					direction = DOWN;
					HardwareIn_MoveDown(SLOW);
				}
			}
		}
	}
	else if (isCabinMoving == TRUE)
	{
		// continue on..
	}
}

/*
this function is triggered by buttons in the elevator cabin, the hall buttons at each floor, OR by the external application. this flags a floor as to require a visit and notifies the external application that we're moving to that floor, unless the cabin is already there OR it has already been flagged.
*/
void GoTo (int floor)
{
	BOOL alreadyThere = (currentFloor == floor && isCabinMoving == FALSE);
	if (alreadyThere == FALSE && floorCalls[floor] == FALSE)
	{
		floorCalls[floor] = TRUE;
		HardwareIn_SetLed(floor);
		AbstractApplication_MovingTo(floor); // notify the external application
	}
}

/*
this function is triggered by the emergency button in the cabin OR by the external application. if it's not already the case, this function puts the system into emergency mode and notifies the external application that there is an emergency.
*/
void CallEmergency ()
{
	if (emergency == FALSE)
	{
		emergency = TRUE;
		AbstractApplication_NewEmergency(); // notify the external application
	}
}

/*
this function is triggered by the emergency button in the cabin OR by the external application. if it's not already the case, this function puts the system into emergency mode and notifies the external application that there is an emergency.
*/
void ResumeOperations ()
{
	if (emergency == TRUE)
	{
		emergency = FALSE;
		AbstractApplication_ResumeOperations(); // notify the external application
	}
}

/*
this function is triggered by the sensors, right above each floor. if we're moving down (nearing the floor) AND we need to stop there (because it's the lowest floor, or the floor has been marked as to require a visit), we prepare to stop by slowing down. if we're moving up (away from the floor) we speed up.
*/
void Above (int floor)
{
	BOOL weNeedToStopAtThisFloor = (floorCalls[floor] == TRUE || floor == LOWEST_FLOOR);
	
	if (direction == DOWN && weNeedToStopAtThisFloor)
	{
		HardwareIn_MoveDown(SLOW);
		preparingToStop = TRUE; // we will be stopping
	}
	else if (direction == UP)
	{
		HardwareIn_MoveUp(FAST);
	}
}

/*
this function is triggered by sensors at each floor. if we're just passing by, we update the floor display number. if we need to stop, we also stop, mark the floor as visited, start a timer to wait for a specified minimum time, turn off the LED indicating that the elevator is coming and notify the external application that we arrived at the floor.
*/
void At (int floor)
{
	initializing = FALSE;
	
	// if we prepared to stop, stop
	if (preparingToStop == TRUE)
	{
		HardwareIn_StopMoving();
		isCabinMoving = FALSE;
		preparingToStop = FALSE;
		CountdownTimer_StartTimer(VISIT_TIMER_ID, MINIMUM_VISIT_TIME); // start a timer that's used to make sure we've waited at this floor for (at least) a specified minimum time
		floorCalls[floor] = FALSE; // we've been here now
		HardwareIn_UnsetLed(floor);
		AbstractApplication_ArrivedAt(floor); // notify the external application
	}
	currentFloor = floor; // we arrived here
	HardwareIn_SetNumberDisplay(floor); // update the floor number on the display
}

/*
this function is triggered by the sensors, right below each floor. if we're moving up (nearing the floor) AND we need to stop there (because the floor has been marked as to require a visit), we prepare to stop by slowing down. if we're moving down (away from the floor) we speed up.
*/
void Below (int floor)
{
	BOOL weNeedToStopAtThisFloor = (floorCalls[floor] == TRUE);
	if (direction == UP && floorCalls[floor] == TRUE)
	{
		HardwareIn_MoveUp(SLOW);
		preparingToStop = TRUE;
	}
	else if (direction == DOWN)
	{
		HardwareIn_MoveDown(FAST);
	}
}
